package com.gbas.gnet.server;

import com.gbas.gnet.*;
import org.apache.log4j.Logger;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Vector;

/**
 * @author Gbas
 * @version 1.0
 */
public class ServerThread extends Thread {
    static Logger logger = Logger.getLogger(ServerThread.class);
    /* Responsable del servidor */
    NetListenerServer fm = null;

    /* Clientes conectados */
    Vector<ClientThread> clients = null;

    static final int DEFAULT_PORT = 6677;
    /* Puerto de escucha */
    public int port = DEFAULT_PORT;

    /* tarea de lanzamiento de pings a clietnes */
    PingALive pingsThread = null;
    Thread pingLaunch = null;
    public static final boolean PINGSYSTEM = true;


    /* Socket de entrada */
    ServerSocket ss = null;

    /* Contador autoincremental para nombres de tareas de servidor */
    int nConnect = 0;
    private boolean runServer = true;
    boolean changePort = true;

    public ServerThread(NetListenerServer fm) {
        super();
        this.fm = fm;
        nConnect = 0;
        runServer = true;

        resetStatusServer();

        if (PINGSYSTEM)
            startPing();
    }

    public boolean isRunServer() {
        return runServer;
    }

    public void setRunServer(boolean runServer) {
        this.runServer = runServer;
    }

    public void resetStatusServer() {
        logger.debug("Reseteado los datos del server");
        clients = new Vector<ClientThread>();
        nConnect = 0;

    }

    public void startPing() {
        logger.debug("Arrancado el sistema de pings");
        pingsThread = new PingALive(this);
        pingLaunch = new Thread(pingsThread);
        pingLaunch.start();
    }

    public void run() {
        while (changePort) {
            try {
                ss = new ServerSocket(port);
                logger.info("Servidor a la escucha en puerto " + port);

                while (runServer) {
                    Socket socket = ss.accept();
                    logger.debug("Recibida peticion de connexion");

                    ClientThread cl = new ClientThread(fm, socket, "Server-thread-" + (++nConnect));
                    cl.start();
                    logger.debug("Tarea cliente creada y lanzada");

                    clients.add(cl);
                    logger.info("Conectado nuevo cliente " + cl.getName());

                    cl.send(new NetObjectOpen());
                }
            } catch (java.net.BindException be) {
                logger.info("Puerto " + port + " ocupado");

                if (changePort) {
                    port++;
                    logger.info("probando siguiente..." + port);
                }
            } catch (Exception e) {
                logger.warn("Error... cerrando conexiones ");
                close();
                e.printStackTrace();
            }
        }
    }

    public synchronized void sendToAll(NetObject nObj) {
        //    log("Enviando mensaje a todos");
        logger.debug("Enviando objeto " + nObj.getClass().toString() + " a todos los clientes");
        for (ClientThread th : clients) {
            try {
                th.send(nObj);
            } catch (Exception e) {
                remove(th);
            }
        }
    }

    public synchronized void sendToAllExcept(NetObject nObj, ClientThread origen) {
        logger.warn("Enviando objeto " + nObj.getClass().toString() + " a todos los clientes excepto origen");
        //    log("Enviando mensaje a todos menos uno");
        for (ClientThread th : clients) {
            if (th != origen) {
                try {
                    th.send(nObj);
                } catch (Exception e) {
                    remove(th);
                }
            }
        }
    }

    public synchronized void remove(ClientThread th) {
//    th.close();

        if (clients.indexOf(th) != -1) {
            //      fm.netEventClose(th);
            if (clients.remove(th)) {
                logger.info("Eliminado cliente " + th.toString());
            } else {
                logger.warn("Imposible eliminar cliente " + th.toString());
            }
        }

        logger.info("" + numClients() + " conexiones activas");
    }

    public synchronized int numClients() {
        return clients.size();
    }

    public Vector<ClientThread> getClients() {
        return clients;
    }

    public synchronized void close() {
        this.sendToAll(new NetObjectClose());
        logger.info("Cerrando server");
        try {
            ss.close();
            ss = null;

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param clients2
     * @param obj
     */
    public void sendToAll(List<ClientThread>clients2, NetObject obj) {
        for (ClientThread th : clients2) {
            try {
                th.send(obj);
            } catch (Exception e) {
                remove(th);
            }
        }
    }

    public void pingLaunched(int i) {
        logger.debug("Enviado ping a todos los clientes ");
        fm.netEventPingLaunch(i);
    }
}
